﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using Microsoft.Kinect;
using System.IO;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using System.Threading;

using PewPew.Logic;
using PewPew;
using System.Windows.Threading;
using System.ComponentModel;
using System.Windows;

namespace PewPew
{
    public partial class MainWindow : Window
    {
        private const int FIND_QR_MODULO = 1;

        private int findQrCounter = 0;
        private KinectSensor sensor;
        private WriteableBitmap colorBitmap;
        private CoordinateMapper coordinateMapper;
        private byte[] colorPixels;
        private Ellipse currSavedTarget;
        private bool targetAppears = false;
        private Game currGame = null;
        private short[] depthPixels;
        private DepthImagePixel[] depthImagePixels;
        private ColorImagePoint[] colorCoordinates;
        private PewPew.QrPoint[] QrPoints;

        private ColorImageFormat colorImageFormat = ColorImageFormat.RgbResolution1280x960Fps12;
        private DepthImageFormat depthImageFormat = DepthImageFormat.Resolution640x480Fps30;

        public MainWindow()
        {
            InitializeComponent();

            this.WindowState = System.Windows.WindowState.Maximized;
        }

        private void dispatcherTimer_Tick(object sender, EventArgs e)
        {
            //Do something

            // Forcing the CommandManager to raise the RequerySuggested event
            CommandManager.InvalidateRequerySuggested();
        }
        
        private static Ellipse generateEllipseOutOfTarget(Target tgt)
        {

            Ellipse ellipse = new Ellipse();
            ellipse.Stroke = tgt.getColor();
            ellipse.Fill = tgt.getColor();

            ellipse.HorizontalAlignment = HorizontalAlignment.Center;
            ellipse.VerticalAlignment = VerticalAlignment.Center;
            ellipse.Width = (int) (2 * tgt.getRadius());
            ellipse.Height = (int) (2 * tgt.getRadius());
            double left = tgt.getX() - (ellipse.Width / 2);
            double top = tgt.getY() - (ellipse.Height / 2);
            ellipse.Margin = new Thickness(left, top, 0, 0);
            return ellipse;
        }

        private void WindowLoaded(object sender, RoutedEventArgs e)
        {
            foreach (var potentialSensor in KinectSensor.KinectSensors)
            {
                if (potentialSensor.Status == KinectStatus.Connected)
                {
                    this.sensor = potentialSensor;
                    break;
                }
            }

            if (null != this.sensor)
            {
                // Turn on the color stream to receive color frames
                this.sensor.ColorStream.Enable(this.colorImageFormat);
                this.sensor.DepthStream.Enable(this.depthImageFormat);

                // Allocate space to put the pixels we'll receive
                this.colorPixels = new byte[this.sensor.ColorStream.FramePixelDataLength];
                this.depthPixels = new short[this.sensor.DepthStream.FramePixelDataLength];
                this.depthImagePixels = new DepthImagePixel[this.sensor.DepthStream.FramePixelDataLength];
                this.colorCoordinates = new ColorImagePoint[this.sensor.DepthStream.FramePixelDataLength];

                this.coordinateMapper = new CoordinateMapper(this.sensor);

                // This is the bitmap we'll display on-screen
                this.colorBitmap = new WriteableBitmap(this.sensor.ColorStream.FrameWidth, this.sensor.ColorStream.FrameHeight, 96.0, 96.0, PixelFormats.Bgr32, null);
                
                // Set the image we display to point to the bitmap where we'll put the image data
                this.Image.Source = this.colorBitmap;

                // Add an event handler to be called whenever there is new color frame data
                //this.sensor.ColorFrameReady += this.SensorColorFrameReady;

                // Add an event handler to be called whenever there is new depth frame data
                //this.sensor.DepthFrameReady += this.SensorDepthFrameReady;
                this.sensor.AllFramesReady += this.SensorAllFramesReady;

                // Start the sensor!
                try
                {
                    this.sensor.Start();
                }
                catch (IOException)
                {
                    this.sensor = null;
                }
            }


            currGame = new Game(PlayCanvas, lblEnemy);

            //  DispatcherTimer setup

            DispatcherTimer targetSummoner = new DispatcherTimer(new TimeSpan(0, 0, 5), DispatcherPriority.Normal, delegate
            {
                HandleTargets();
            }, this.Dispatcher);

            DispatcherTimer targetRemover = new DispatcherTimer(new TimeSpan(0, 0, 2), DispatcherPriority.Normal, delegate
            {
                if (currGame.enemyHit)
                {
                    PlayCanvas.Children.Remove(this.currSavedTarget);
                    currGame.enemyHit = false;
                    targetAppears = false;
                    //this.currSavedTarget = null;
                }
            }, this.Dispatcher);

            //new Thread(addTarget).Start();

            if (null == this.sensor)
            {
                this.lblStatusBar.Content = "No Kinect Found!";
            }
        }

        private void HandleTargets()
        {
            if (targetAppears)
            {
                PlayCanvas.Children.Remove(currSavedTarget);
                targetAppears = false;
            }
            

            Target tgt = currGame.getNextTarget(this.ActualHeight);

            Ellipse currEllipse = generateEllipseOutOfTarget(tgt);
            PlayCanvas.Children.Add(currEllipse);

            this.currSavedTarget = currEllipse;
            targetAppears = true;
        }

        private void WindowClosing(object sender, System.ComponentModel.CancelEventArgs e)
        {
            if (null != this.sensor)
            {
                this.sensor.Stop();
            }
        }

        private void KinectSensorOnAllFramesReady(object sender, AllFramesReadyEventArgs allFramesReadyEventArgs)
        {
            using (var colorImageFrame = allFramesReadyEventArgs.OpenColorImageFrame())
            {
                if (colorImageFrame == null)
                {
                    return;
                }
            }
        }

        private void SensorColorFrameReady(object sender, ColorImageFrameReadyEventArgs e)
        {
            //using (ColorImageFrame colorFrame = e.OpenColorImageFrame())
            //{
            //    if (colorFrame != null)
            //    {
            //        // Copy the pixel data from the image to a temporary array
            //        colorFrame.CopyPixelDataTo(this.colorPixels);

            //        // Write the pixel data into our bitmap
            //        this.colorBitmap.WritePixels(
            //            new Int32Rect(0, 0, this.colorBitmap.PixelWidth, this.colorBitmap.PixelHeight),
            //            this.colorPixels,
            //            this.colorBitmap.PixelWidth * sizeof(int),
            //            0);

            //        if (findQrCounter % FIND_QR_MODULO == 0)
            //        {
            //            var result = QrProcessor.ProcessQRInKinectImage(this.colorBitmap);
            //            if (result != null && result.Text != String.Empty)
            //            {
            //                this.QrPoints = PewPew.QrPoint.CreateFromZXingArray(result.ResultPoints, colorFrame.Width, colorFrame.Height);

            //                // Draw hit point 
            //                bool isTargetHit = false;
            //                QrPoint avgPoint = QrPoint.AveragePoint(this.QrPoints);
            //                if (avgPoint != null)
            //                {
            //                    int x = ((int)avgPoint.X * (int)this.ActualWidth) / colorFrame.Width;
            //                    int y = (int)this.ActualHeight - ((int)avgPoint.Y * (int)this.ActualHeight) / colorFrame.Height;
            //                    System.Drawing.Point pnt = new System.Drawing.Point(x, y);

            //                    currGame.DrawHitPoint(pnt, Target.EnemyTypeByInputText(result.Text).color);
            //                    isTargetHit = currGame.isHit(currGame.getCurrTarget(), pnt, result.Text, this.targetAppears); 
            //                }

            //                if (isTargetHit)
            //                {
            //                    handleHit();
            //                }

            //                lblQrText.Content = result.Text;
            //            }
            //        }
            //        findQrCounter++;
            //    }
            //}
        }

        private void handleHit()
        {
            if (this.currSavedTarget != null)
            {
                this.currSavedTarget.Stroke = System.Windows.Media.Brushes.Black;
                this.currSavedTarget.Fill = System.Windows.Media.Brushes.Black;
            }
            
            // to make sure enemy will be removed
            currGame.enemyHit = true;
            
            
            // play explosion audio 
            updateScore();
            
        }

        private void updateScore()
        {
            currGame.score += 100;
            lblScore.Content = currGame.score;
        }

        private void SensorDepthFrameReady(object sender, DepthImageFrameReadyEventArgs e)
        {
            using (DepthImageFrame depthFrame = e.OpenDepthImageFrame())
            {
                if (depthFrame != null && this.QrPoints != null)
                {
                    depthFrame.CopyPixelDataTo(this.depthPixels);
                    depthFrame.CopyDepthImagePixelDataTo(this.depthImagePixels);

                    this.coordinateMapper.MapDepthFrameToColorFrame(this.depthImageFormat, this.depthImagePixels, this.colorImageFormat, this.colorCoordinates);

                    for (var i = 0; i < this.QrPoints.Length; i++)
                    {
                        if (this.QrPoints[i] != null)
                        {
                            this.QrPoints[i].FixZByDepthPixels(this.depthImagePixels, depthFrame.Width);
                        }
                    }

                    var points_str = "";
                    for (var i = 0; i < this.QrPoints.Length; i++)
                    {
                        if (this.QrPoints[i] != null)
                        {
                            points_str += "X:\t" + this.QrPoints[i].X.ToString() + "\t";
                            points_str += "Y:\t" + this.QrPoints[i].Y.ToString() + "\t";
                            points_str += "Z:\t" + this.QrPoints[i].Z.ToString() + "\n";
                        }

                        this.QrPoints[i] = null;
                    }
                    if (points_str != String.Empty)
                    {
                        lblQrPoints.Content = points_str;
                    }
                }
            }
        }

        private void SensorAllFramesReady(object sender, AllFramesReadyEventArgs e)
        {
            DepthImageFrame depthFrame = e.OpenDepthImageFrame();
            ColorImageFrame colorFrame = e.OpenColorImageFrame();

            if (depthFrame == null)
            {
                return;
            }

            if (colorFrame != null)
            {
                // Copy the pixel data from the image to a temporary array
                colorFrame.CopyPixelDataTo(this.colorPixels);

                // Write the pixel data into our bitmap
                this.colorBitmap.WritePixels(
                    new Int32Rect(0, 0, this.colorBitmap.PixelWidth, this.colorBitmap.PixelHeight),
                    this.colorPixels,
                    this.colorBitmap.PixelWidth * sizeof(int),
                    0);

                if (findQrCounter % FIND_QR_MODULO == 0)
                {
                    var result = QrProcessor.ProcessQRInKinectImage(this.colorBitmap);
                    if (result != null && result.Text != String.Empty)
                    {
                        this.QrPoints = PewPew.QrPoint.CreateFromZXingArray(result.ResultPoints, colorFrame.Width, colorFrame.Height);

                        if (!PewPew.QrPoint.LegalPointSet(this.QrPoints))
                        {
                            return;
                        }

                        // Save to history
                        QrPoint.UpdateHistory(QrPoints);

                        // Draw hit point 
                        bool isTargetHit = false;
                        QrPoint avgPoint = QrPoint.AveragePoint(this.QrPoints);
                        if (avgPoint != null)
                        {
                            int x = ((int)avgPoint.X * (int)this.ActualWidth) / colorFrame.Width;
                            int y = (int)this.ActualHeight - ((int)avgPoint.Y * (int)this.ActualHeight) / colorFrame.Height;
                            System.Drawing.Point pnt = new System.Drawing.Point(x, y);
                            var enemy = Target.EnemyTypeByInputText(result.Text);
                            if (enemy != null)
                            {
                                currGame.DrawHitPoint(pnt, Target.EnemyTypeByInputText(result.Text).color);
                                isTargetHit = currGame.isHit(currGame.getCurrTarget(), pnt, result.Text, this.targetAppears);
                            }
                        }
                        if (isTargetHit)
                        {
                            handleHit();
                        }

                        lblQrText.Content = result.Text;
                    }
                }
                findQrCounter++;

                colorFrame.Dispose();
            }

            if (depthFrame != null && this.QrPoints != null)
            {
                depthFrame.CopyPixelDataTo(this.depthPixels);
                depthFrame.CopyDepthImagePixelDataTo(this.depthImagePixels);
                this.coordinateMapper.MapDepthFrameToColorFrame(this.depthImageFormat, this.depthImagePixels, this.colorImageFormat, this.colorCoordinates);

                for (var i = 0; i < this.QrPoints.Length; i++)
                {
                    if (this.QrPoints[i] != null)
                    {
                        this.QrPoints[i].FixZByDepthPixels(this.depthImagePixels, depthFrame.Width);
                    }
                }

                var points_str = "";
                for (var i = 0; i < this.QrPoints.Length; i++)
                {
                    if (this.QrPoints[i] != null)
                    {
                        points_str += "X:\t" + this.QrPoints[i].X.ToString() + "\t";
                        points_str += "Y:\t" + this.QrPoints[i].Y.ToString() + "\t";
                        points_str += "Z:\t" + this.QrPoints[i].Z.ToString() + "\n";
                    }

                    this.QrPoints[i] = null;
                }
                if (points_str != String.Empty)
                {
                    lblQrPoints.Content = points_str;
                }

                depthFrame.Dispose();
            }
        }
    }
}
